/*
===========================================================================

Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.

This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").

Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/

#include "Precompiled.h"
#include "globaldata.h"


#include "z_zone.h"
#include "doomdef.h"
#include "p_local.h"

#include "s_sound.h"


// State.
#include "doomstat.h"
#include "r_state.h"

// Data.
#include "dstrings.h"
#include "sounds.h"


extern bool globalNetworking;

//
// VERTICAL DOORS
//

//
// T_VerticalDoor
//
void T_VerticalDoor( vldoor_t* door )
{
	result_e	res;

	switch( door->direction )
	{
		case 0:
			// WAITING
			if( !--door->topcountdown )
			{
				switch( door->type )
				{
					case blazeRaise:
						door->direction = -1; // time to go back down
						S_StartSound( &door->sector->soundorg,
									  sfx_bdcls );
						break;

					case normal:
						door->direction = -1; // time to go back down
						S_StartSound( &door->sector->soundorg,
									  sfx_dorcls );
						break;

					case close30ThenOpen:
						door->direction = 1;
						S_StartSound( &door->sector->soundorg,
									  sfx_doropn );
						break;

					default:
						break;
				}
			}
			break;

		case 2:
			//  INITIAL WAIT
			if( !--door->topcountdown )
			{
				switch( door->type )
				{
					case raiseIn5Mins:
						door->direction = 1;
						door->type = normal;
						S_StartSound( &door->sector->soundorg,
									  sfx_doropn );
						break;

					default:
						break;
				}
			}
			break;

		case -1:
			// DOWN
			res = T_MovePlane( door->sector,
							   door->speed,
							   door->sector->floorheight,
							   false, 1, door->direction );
			if( res == pastdest )
			{
				switch( door->type )
				{
					case blazeRaise:
					case blazeClose:
						door->sector->specialdata = NULL;
						P_RemoveThinker( &door->thinker ); // unlink and free
						S_StartSound( &door->sector->soundorg,
									  sfx_bdcls );
						break;

					case normal:
					case closed:
						door->sector->specialdata = NULL;
						P_RemoveThinker( &door->thinker ); // unlink and free
						break;

					case close30ThenOpen:
						door->direction = 0;
						door->topcountdown = TICRATE * 30;
						break;

					default:
						break;
				}
			}
			else if( res == crushed )
			{
				switch( door->type )
				{
					case blazeClose:
					case closed:		// DO NOT GO BACK UP!
						break;

					default:
						door->direction = 1;
						S_StartSound( &door->sector->soundorg,
									  sfx_doropn );
						break;
				}
			}
			break;

		case 1:
			// UP
			res = T_MovePlane( door->sector,
							   door->speed,
							   door->topheight,
							   false, 1, door->direction );

			if( res == pastdest )
			{
				switch( door->type )
				{
					case blazeRaise:
					case normal:
						door->direction = 0; // wait at top
						door->topcountdown = door->topwait;
						break;

					case close30ThenOpen:
					case blazeOpen:
					case opened:
						door->sector->specialdata = NULL;
						P_RemoveThinker( &door->thinker ); // unlink and free
						break;

					default:
						break;
				}
			}
			break;
	}
}


//
// EV_DoLockedDoor
// Move a locked door up/down
//

int
EV_DoLockedDoor
( line_t*	line,
  vldoor_e	type,
  mobj_t*	thing )
{
	player_t*	p;

	p = thing->player;

	if( !p )
	{
		return 0;
	}

	switch( line->special )
	{
		case 99:	// Blue Lock
		case 133:
			if( !p )
			{
				return 0;
			}
			if( !p->cards[it_bluecard] && !p->cards[it_blueskull] )
			{
				p->message = PD_BLUEO;
				if( p == &::g->players[::g->consoleplayer] )
				{
					S_StartSound( NULL, sfx_oof );
				}
				return 0;
			}
			break;

		case 134: // Red Lock
		case 135:
			if( !p )
			{
				return 0;
			}
			if( !p->cards[it_redcard] && !p->cards[it_redskull] )
			{
				p->message = PD_REDO;
				if( p == &::g->players[::g->consoleplayer] )
				{
					S_StartSound( NULL, sfx_oof );
				}
				return 0;
			}
			break;

		case 136:	// Yellow Lock
		case 137:
			if( !p )
			{
				return 0;
			}
			if( !p->cards[it_yellowcard] &&
					!p->cards[it_yellowskull] )
			{
				p->message = PD_YELLOWO;
				if( p == &::g->players[::g->consoleplayer] )
				{
					S_StartSound( NULL, sfx_oof );
				}
				return 0;
			}
			break;
	}

	return EV_DoDoor( line, type );
}


int
EV_DoDoor
( line_t*	line,
  vldoor_e	type )
{
	int		secnum, rtn;
	sector_t*	sec;
	vldoor_t*	door;

	secnum = -1;
	rtn = 0;

	while( ( secnum = P_FindSectorFromLineTag( line, secnum ) ) >= 0 )
	{
		sec = &::g->sectors[secnum];
		if( sec->specialdata )
		{
			continue;
		}


		// new door thinker
		rtn = 1;
		door = ( vldoor_t* )DoomLib::Z_Malloc( sizeof( *door ), PU_LEVEL, 0 );
		P_AddThinker( &door->thinker );
		sec->specialdata = door;

		door->thinker.function.acp1 = ( actionf_p1 ) T_VerticalDoor;
		door->sector = sec;
		door->type = type;
		door->topwait = VDOORWAIT;
		door->speed = VDOORSPEED;

		switch( type )
		{
			case blazeClose:
				door->topheight = P_FindLowestCeilingSurrounding( sec );
				door->topheight -= 4 * FRACUNIT;
				door->direction = -1;
				door->speed = VDOORSPEED * 4;
				S_StartSound( &door->sector->soundorg,
							  sfx_bdcls );
				break;

			case closed:
				door->topheight = P_FindLowestCeilingSurrounding( sec );
				door->topheight -= 4 * FRACUNIT;
				door->direction = -1;
				S_StartSound( &door->sector->soundorg,
							  sfx_dorcls );
				break;

			case close30ThenOpen:
				door->topheight = sec->ceilingheight;
				door->direction = -1;
				S_StartSound( &door->sector->soundorg,
							  sfx_dorcls );
				break;

			case blazeRaise:
			case blazeOpen:
				door->direction = 1;
				door->topheight = P_FindLowestCeilingSurrounding( sec );
				door->topheight -= 4 * FRACUNIT;
				door->speed = VDOORSPEED * 4;
				if( door->topheight != sec->ceilingheight )
					S_StartSound( &door->sector->soundorg,
								  sfx_bdopn );
				break;

			case normal:
			case opened:
				door->direction = 1;
				door->topheight = P_FindLowestCeilingSurrounding( sec );
				door->topheight -= 4 * FRACUNIT;
				if( door->topheight != sec->ceilingheight )
					S_StartSound( &door->sector->soundorg,
								  sfx_doropn );
				break;

			default:
				break;
		}

	}
	return rtn;
}


//
// EV_VerticalDoor : open a door manually, no tag value
//
void
EV_VerticalDoor
( line_t*	line,
  mobj_t*	thing )
{
	player_t*	player;
	int		secnum;
	sector_t*	sec;
	vldoor_t*	door;
	int		side;

	side = 0;	// only front ::g->sides can be used

	//	Check for locks
	player = thing->player;

	switch( line->special )
	{
		case 26: // Blue Lock
		case 32:
			if( !player )
			{
				return;
			}

			if( !player->cards[it_bluecard] && !player->cards[it_blueskull] )
			{
				player->message = PD_BLUEK;
				if( globalNetworking || ( player == &::g->players[::g->consoleplayer] ) )
				{
					S_StartSound( player->mo, sfx_oof );
				}
				return;
			}
			break;

		case 27: // Yellow Lock
		case 34:
			if( !player )
			{
				return;
			}

			if( !player->cards[it_yellowcard] &&
					!player->cards[it_yellowskull] )
			{
				player->message = PD_YELLOWK;
				if( globalNetworking || ( player == &::g->players[::g->consoleplayer] ) )
				{
					S_StartSound( player->mo, sfx_oof );
				}
				return;
			}
			break;

		case 28: // Red Lock
		case 33:
			if( !player )
			{
				return;
			}

			if( !player->cards[it_redcard] && !player->cards[it_redskull] )
			{
				player->message = PD_REDK;
				if( globalNetworking || ( player == &::g->players[::g->consoleplayer] ) )
				{
					S_StartSound( player->mo, sfx_oof );
				}
				return;
			}
			break;
	}

	// if the sector has an active thinker, use it
	sec = ::g->sides[ line->sidenum[side ^ 1]] .sector;
	secnum = sec -::g->sectors;

	if( sec->specialdata )
	{
		door = ( vldoor_t* )sec->specialdata;
		switch( line->special )
		{
			case	1: // ONLY FOR "RAISE" DOORS, NOT "OPEN"s
			case	26:
			case	27:
			case	28:
			case	117:
				if( door->direction == -1 )
				{
					door->direction = 1;    // go back up
				}
				else
				{
					if( !thing->player )
					{
						return;    // JDC: bad guys never close doors
					}

					door->direction = -1;	// start going down immediately
				}
				return;
		}
	}

	// for proper sound
	if( globalNetworking || ( player == &::g->players[::g->consoleplayer] ) )
	{
		switch( line->special )
		{
			case 117:	// BLAZING DOOR RAISE
			case 118:	// BLAZING DOOR OPEN
				S_StartSound( &sec->soundorg, sfx_bdopn );
				break;

			case 1:	// NORMAL DOOR SOUND
			case 31:
				S_StartSound( &sec->soundorg, sfx_doropn );
				break;

			default:	// LOCKED DOOR SOUND
				S_StartSound( &sec->soundorg, sfx_doropn );
				break;
		}
	}


	// new door thinker
	door = ( vldoor_t* )DoomLib::Z_Malloc( sizeof( *door ), PU_LEVEL, 0 );
	P_AddThinker( &door->thinker );
	sec->specialdata = door;
	door->thinker.function.acp1 = ( actionf_p1 ) T_VerticalDoor;
	door->sector = sec;
	door->direction = 1;
	door->speed = VDOORSPEED;
	door->topwait = VDOORWAIT;

	switch( line->special )
	{
		case 1:
		case 26:
		case 27:
		case 28:
			door->type = normal;
			break;

		case 31:
		case 32:
		case 33:
		case 34:
			door->type = opened;
			line->special = 0;
			break;

		case 117:	// blazing door raise
			door->type = blazeRaise;
			door->speed = VDOORSPEED * 4;
			break;
		case 118:	// blazing door open
			door->type = blazeOpen;
			line->special = 0;
			door->speed = VDOORSPEED * 4;
			break;
	}

	// find the top and bottom of the movement range
	door->topheight = P_FindLowestCeilingSurrounding( sec );
	door->topheight -= 4 * FRACUNIT;
}


//
// Spawn a door that closes after 30 seconds
//
void P_SpawnDoorCloseIn30( sector_t* sec )
{
	vldoor_t*	door;

	door = ( vldoor_t* )DoomLib::Z_Malloc( sizeof( *door ), PU_LEVEL, 0 );

	P_AddThinker( &door->thinker );

	sec->specialdata = door;
	sec->special = 0;

	door->thinker.function.acp1 = ( actionf_p1 )T_VerticalDoor;
	door->sector = sec;
	door->direction = 0;
	door->type = normal;
	door->speed = VDOORSPEED;
	door->topcountdown = 30 * TICRATE;
}

//
// Spawn a door that opens after 5 minutes
//
void
P_SpawnDoorRaiseIn5Mins
( sector_t*	sec,
  int		secnum )
{
	vldoor_t*	door;

	door = ( vldoor_t* )DoomLib::Z_Malloc( sizeof( *door ), PU_LEVEL, 0 );

	P_AddThinker( &door->thinker );

	sec->specialdata = door;
	sec->special = 0;

	door->thinker.function.acp1 = ( actionf_p1 )T_VerticalDoor;
	door->sector = sec;
	door->direction = 2;
	door->type = raiseIn5Mins;
	door->speed = VDOORSPEED;
	door->topheight = P_FindLowestCeilingSurrounding( sec );
	door->topheight -= 4 * FRACUNIT;
	door->topwait = VDOORWAIT;
	door->topcountdown = 5 * 60 * TICRATE;
}



// UNUSED
// Separate into p_slidoor.c?


